﻿#include "FontSubsystem.h"

#include <cmath>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftlcdfil.h>

#include "Object/NewObject.h"
#include "Path/Paths.h"

#include "Font/Glyph.h"

#include "Framework/Application.h"

#include "Output/Console.h"
#include "Font/TextTexturePool.h"

static RFontApplicationSubsystem* Instance=nullptr;

void RFontApplicationSubsystem::Initialize()
{
   Instance=this;

   auto Error=FT_Init_FreeType((FT_Library*)&LowLevelFreeTypeLibrary);

   if(GetApplication()->GetInitializeArguments().DefaultFont.Exists())
   {
      DefaultFont=LoadFontFromFile(GetApplication()->GetInitializeArguments().DefaultFont);
   }

   if(GetApplication()->GetInitializeArguments().FallBackFont.Exists())
   {
      FallBackFont=LoadFontFromFile(GetApplication()->GetInitializeArguments().FallBackFont);
   }

   if(!DefaultFont)
   {
      WH::PrintLine(U"没有设置默认字体，无法显示任何文本");
   }
   else if(!FallBackFont)
   {
      FallBackFont=DefaultFont;
   }
   
   TextTexturePool=NewObject<RTextTexturePool>(this);
}

void RFontApplicationSubsystem::Deinitialize() 
{
   Instance=nullptr;

   FT_Done_FreeType((FT_Library)LowLevelFreeTypeLibrary);

}

RFontApplicationSubsystem *RFontApplicationSubsystem::Get()
{
    return Instance;
}

RFontApplicationSubsystemPtr RFontApplicationSubsystem::GetInstance()
{
   return Instance;
}

TSharedObjectPtr<RFont> RFontApplicationSubsystem::LoadFontFromFile(const SPath &FontFile)
{
    auto NewFont=NewObject<RFont>(this);
   
    auto FileName8=FontFile.ToString().ToUtf8();

    auto Error=FT_New_Face((FT_Library)LowLevelFreeTypeLibrary,(const char * )FileName8.Data() ,0,(FT_Face*)&NewFont->LowLevelFont);
    assert(Error==0&& "Cant load font file, may be not exist");
   return NewFont;
}

TSharedObjectPtr<RFont> RFontApplicationSubsystem::LoadFontFromBinary(const TVector<uint8_t> &InFontBinary)
{
   auto NewFont=NewObject<RFont>(this);
    
   auto Error=FT_New_Memory_Face(
      (FT_Library)LowLevelFreeTypeLibrary
      ,InFontBinary.Data()
      ,InFontBinary.Num()
      ,0
      ,(FT_Face*)&NewFont->LowLevelFont);
   assert(Error==0&& "Cant load font file, may be not exist");
    return NewFont;
}

SIntPoint RFontApplicationSubsystem::GetOccupiedSize(const CString & Text,TSharedObjectPtr<RFont> InFont,uint32_t FontSize)
{
    auto Unicodes=Text.GetUnicodes();

    TVector<FT_Vector> Positions;
    Positions.Resize(Unicodes.Num()-1);
    SIntPoint Pen(0,0);

   //calculate glyph position
   for(size_t CharacterIndex=0;CharacterIndex<Unicodes.Num()-1;CharacterIndex++)
   {
      Positions[CharacterIndex].x=Pen.X;
      Positions[CharacterIndex].y=Pen.Y;

      std::shared_ptr<SFreeTypeGlyph> Glyph=InFont->SupportChar(Unicodes[CharacterIndex])?
          InFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize)
         :FallBackFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize);
      auto AdvanceInPixel=FT_Glyph(Glyph->Glyph)->advance.x >>16 ;
      Pen.X+= AdvanceInPixel;
   } 

    //calculate image size
   FT_BBox ImageSize;
   {

      ImageSize.xMax=-std::numeric_limits<decltype(ImageSize.xMax)>::max(); 
      ImageSize.yMax=-std::numeric_limits<decltype(ImageSize.xMax)>::max();   

      ImageSize.xMin=std::numeric_limits<decltype(ImageSize.xMax)>::max();
      ImageSize.yMin=std::numeric_limits<decltype(ImageSize.xMax)>::max();

      FT_BBox GlphyBox;
     
      
      for(size_t CharacterIndex=0;CharacterIndex<Unicodes.Num()-1;CharacterIndex++)
      {

         std::shared_ptr<SFreeTypeGlyph> Glyph=InFont->SupportChar(Unicodes[CharacterIndex])?
            InFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize)
            :FallBackFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize);
            
         FT_Glyph_Get_CBox(FT_Glyph(Glyph->Glyph),FT_GLYPH_BBOX_PIXELS,&GlphyBox);

         GlphyBox.xMax+=Positions[CharacterIndex].x;
         GlphyBox.xMin+=Positions[CharacterIndex].x;         

         GlphyBox.yMax+=Positions[CharacterIndex].y;
         GlphyBox.yMin+=Positions[CharacterIndex].y;  

         ImageSize.xMax=CMathBase::Max(GlphyBox.xMax,ImageSize.xMax);
         ImageSize.yMax=CMathBase::Max(GlphyBox.yMax,ImageSize.yMax);    

         ImageSize.xMin=CMathBase::Min(GlphyBox.xMin,ImageSize.xMin);
         ImageSize.yMin=CMathBase::Min(GlphyBox.yMin,ImageSize.yMin);  
      }

   }

   auto RowNum=ImageSize.yMax-ImageSize.yMin;
   auto ColumnNum=ImageSize.xMax-ImageSize.xMin;

   return SIntPoint(ColumnNum,RowNum);
}

float RFontApplicationSubsystem::GetSingleLineOccupiedWidth(const CString &Text, TSharedObjectPtr<RFont> InFont, uint32_t FontSize)
{
   auto Unicodes=Text.GetUnicodes();

   int PenX=0;

   //calculate glyph position
   for(int64_t CharacterIndex=0;
      CharacterIndex<(int64_t)(Unicodes.Num())-1
      ;CharacterIndex++)
   {
      std::shared_ptr<SFreeTypeGlyph> Glyph=InFont->SupportChar(Unicodes[CharacterIndex])?
          InFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize)
         :FallBackFont->FindOrCreateGlyph(Unicodes[CharacterIndex],FontSize);
      auto AdvanceInPixel=FT_Glyph(Glyph->Glyph)->advance.x >>16 ;
      PenX+= AdvanceInPixel;
   } 

   return PenX;
}

int64_t RFontApplicationSubsystem::CalculateCursorIndex(const CString &LineText, TSharedObjectPtr<RFont> InFont, uint32_t FontSize, float CursorX)
{
   int64_t TotalTextLength=LineText.GetLength();
   int64_t ColumnIndex=0;
   float LastTextWidth=0;
   for(int64_t PotentialColumnIndex=1;PotentialColumnIndex<=TotalTextLength;++PotentialColumnIndex)
   {
      CString FormerContent=
         TotalTextLength> PotentialColumnIndex ?
         LineText.SubString(0,PotentialColumnIndex)
         : LineText
         ;
      float FormerWidth=RFontApplicationSubsystem::GetInstance()->
      GetOccupiedSize(
         FormerContent
         ,InFont
         ,FontSize).X;
      if(CursorX>FormerWidth)
      {
         ColumnIndex=PotentialColumnIndex;
         LastTextWidth=FormerWidth;
         continue;
      }

      if(CursorX<=FormerWidth)
      {
         float LastCharWidth=FormerWidth-LastTextWidth;
         if(CursorX<(LastTextWidth+LastCharWidth/2))
         {
               ColumnIndex=PotentialColumnIndex-1;
         }
         else
         {
               ColumnIndex=PotentialColumnIndex;
         }
         break;
      }
   }

   return ColumnIndex;
}

RFontApplicationSubsystem::SCursorPosition RFontApplicationSubsystem::CalculateCursorPosition(const CString &LineText, TSharedObjectPtr<RFont> InFont, uint32_t FontSize, float MouseX)
{
   
   int64_t TotalTextLength=LineText.GetLength();
   int64_t ColumnIndex=0;
   float LastTextWidth=0;
   for(int64_t PotentialColumnIndex=1;PotentialColumnIndex<=TotalTextLength;++PotentialColumnIndex)
   {
      CString FormerContent=
         TotalTextLength> PotentialColumnIndex ?
         LineText.SubString(0,PotentialColumnIndex)
         : LineText
         ;
      float FormerWidth=RFontApplicationSubsystem::GetInstance()->
      GetOccupiedSize(
         FormerContent
         ,InFont
         ,FontSize).X;
      if(MouseX>FormerWidth)
      {
         ColumnIndex=PotentialColumnIndex;
         LastTextWidth=FormerWidth;
         continue;
      }

      if(MouseX<=FormerWidth)
      {
         float LastCharWidth=FormerWidth-LastTextWidth;
         if(MouseX<(LastTextWidth+LastCharWidth/2))
         {
            ColumnIndex=PotentialColumnIndex-1;

         }
         else
         {
            ColumnIndex=PotentialColumnIndex;
            LastTextWidth=FormerWidth;
         }
         break;
      }
   }

   return SCursorPosition(ColumnIndex,LastTextWidth);
}

TSharedObjectPtr<RFont> RFontApplicationSubsystem::GetDefaultFont()
{
   assert(DefaultFont&&U"未设置默认字体，无法使用默认字体");
   return DefaultFont;
}
